﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows;

namespace Demos.Puzzles.WolfGoatCabbage
{
    public delegate void FinDeTrayectoHandler();
    public enum Orillas { izquierda, derecha };
    public enum Personajes { lobo, cabra, col, balsa };

    public class Orilla
    {
        public Orillas orilla;
        public bool balsa;
        public SortedSet<Personajes> poblacion;

        public Orilla(Orillas o, bool b, SortedSet<Personajes> p)
        {
            orilla = o;
            balsa = b;
            poblacion = p;
        }
    }

    public class ViewModel : DependencyObject, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private double _velocidadAnimacion;
        public double VelocidadAnimacion
        {
            get { return _velocidadAnimacion; }
            set
            {
                _velocidadAnimacion = value;
                NotifyPropertyChanged("VelocidadAnimacion");
            }
        }

        private bool[] _contenidoRadioButtons;
        public bool[] ContenidoRadioButtons
        {
            get { return _contenidoRadioButtons; }
            set
            {
                _contenidoRadioButtons = value;
                NotifyPropertyChanged("ContenidoRadioButtons");
            }
        }

        private string _contenidoBotonParar;
        public string ContenidoBotonParar
        {
            get { return _contenidoBotonParar; }
            set
            {
                _contenidoBotonParar = value;
                NotifyPropertyChanged("ContenidoBotonParar");
            }
        }


        public double LoboX
        {
            get { return (double)GetValue(LoboXProperty); }
            set { SetValue(LoboXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LoboX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LoboXProperty =
            DependencyProperty.Register("LoboX", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public double LoboY
        {
            get { return (double)GetValue(LoboYProperty); }
            set { SetValue(LoboYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LoboY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LoboYProperty =
            DependencyProperty.Register("LoboY", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public double CabraX
        {
            get { return (double)GetValue(CabraXProperty); }
            set { SetValue(CabraXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CabraX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CabraXProperty =
            DependencyProperty.Register("CabraX", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public double CabraY
        {
            get { return (double)GetValue(CabraYProperty); }
            set { SetValue(CabraYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CabraY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CabraYProperty =
            DependencyProperty.Register("CabraY", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public double ColX
        {
            get { return (double)GetValue(ColXProperty); }
            set { SetValue(ColXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ColX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColXProperty =
            DependencyProperty.Register("ColX", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public double ColY
        {
            get { return (double)GetValue(ColYProperty); }
            set { SetValue(ColYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ColY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColYProperty =
            DependencyProperty.Register("ColY", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public double BalsaX
        {
            get { return (double)GetValue(BalsaXProperty); }
            set { SetValue(BalsaXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BalsaX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BalsaXProperty =
            DependencyProperty.Register("BalsaX", typeof(double), typeof(ViewModel), new UIPropertyMetadata(null));



        public ICommand ComenzarCommand { get; private set; }
        public ICommand PararCommand { get; private set; }


        private bool _viajando;
        private Algoritmia.Problems.Puzzles.WolfGoatCabbageSolver _model;
        public event FinDeTrayectoHandler FinDeTrayecto;
        public bool SeguirViajando { get; set; }
        public Orilla _orillaIzquierda;
        public Orilla _orillaDerecha;
        private int movimientoActual;
        List<Personajes> movimientosSolucion;
        public double PosicionBalsa { get; private set; }
        public double PosicionBalsaContraria { get; private set; }
        public double PosicionOrilla { get; private set; }
        public double PosicionOrillaContraria { get; private set; }
        public Personajes Viajero { get; private set; }
        Func<bool> Viaje;
        private double _duracionViaje; //milisegundos
        private double _duracionSalto; //milisegundos
        const double AlturaSalto = 50; //pixeles


        public ViewModel(Algoritmia.Problems.Puzzles.WolfGoatCabbageSolver model)
        {
            _model = model;
            SeguirViajando = true;
            movimientoActual = 0;
            movimientosSolucion = new List<Personajes>();
            _viajando = false;
            FinDeTrayecto += ViajeTerminado;
            Viaje = SaltarALaBalsa;
            PosicionBalsa = 450;
            PosicionBalsaContraria = 200;
            PosicionOrilla = 730;
            PosicionOrillaContraria = 0;
            _orillaIzquierda = new Orilla(Orillas.izquierda, false, new SortedSet<Personajes>());
            _orillaDerecha = new Orilla(Orillas.derecha, true, new SortedSet<Personajes> 
            { Personajes.lobo, Personajes.cabra, Personajes.col });

            ContenidoBotonParar = "Parar";
            ContenidoRadioButtons = new bool[6];
            ContenidoRadioButtons[1] = true;
            ContenidoRadioButtons[3] = true;
            ContenidoRadioButtons[5] = true;
            VelocidadAnimacion = 1500;
            BalsaX = 450;
            LoboX = 650;
            LoboY = 160;
            CabraX = 700;
            CabraY = 150;
            ColX = 730;
            ColY = 140;

            ComenzarCommand = new RelayCommand(Comenzar, () => !_viajando);
            PararCommand = new RelayCommand(Parar, () => _viajando);
        }


        private void ViajeTerminado()
        {
            _viajando = false;
            movimientoActual = 0;
            movimientosSolucion = new List<Personajes>();
            SeguirViajando = false;
        }

        private void Parar()
        {
            if (SeguirViajando)
            {
                SeguirViajando = false;
                ContenidoBotonParar = "Seguir";
            }
            else
            {
                SeguirViajando = true;
                ContenidoBotonParar = "Parar";
                Solucionar();
            }
        }

        private void Comenzar()
        {
            _viajando = true;
            SeguirViajando = true;
            ObtenerSolucion();
            Solucionar();
        }

        private void CambiarOrilla()
        {
            if (_orillaIzquierda.balsa)
            {
                _orillaIzquierda.balsa = false;
                _orillaIzquierda.poblacion.Remove(Viajero);
                _orillaDerecha.balsa = true;
                _orillaDerecha.poblacion.Add(Viajero);
            }
            else
            {
                _orillaDerecha.balsa = false;
                _orillaDerecha.poblacion.Remove(Viajero);
                _orillaIzquierda.balsa = true;
                _orillaIzquierda.poblacion.Add(Viajero);
            }

            var aux = PosicionBalsa;
            PosicionBalsa = PosicionBalsaContraria;
            PosicionBalsaContraria = aux;

            aux = PosicionOrilla;
            PosicionOrilla = PosicionOrillaContraria;
            PosicionOrillaContraria = aux;
        }

        private void Viajar(Personajes p)
        {
            Viajero = p;
            Viaje();
        }

        private void Solucionar()
        {
            if (movimientoActual < movimientosSolucion.Count)
            {
                Viajar(movimientosSolucion[movimientoActual]);
                movimientoActual++;
            }
            else
            {
                FinDeTrayecto();
            }
        }

        private void ObtenerSolucion()
        {
            var solver = new Algoritmia.Problems.Puzzles.WolfGoatCabbageSolver();
            var solucion = solver.Solve();
            foreach (var viajero in solucion)
            {
                switch (viajero)
                {
                    case "":
                        movimientosSolucion.Add(Personajes.balsa);
                        break;
                    case "cabbage":
                        movimientosSolucion.Add(Personajes.cabra);
                        break;
                    case "wolf":
                        movimientosSolucion.Add(Personajes.lobo);
                        break;
                    case "goat":
                        movimientosSolucion.Add(Personajes.col);
                        break;
                }
            }
        }

        bool SaltarALaBalsa()
        {
            _duracionViaje = 2100 - VelocidadAnimacion;
            _duracionSalto = _duracionViaje / 3;

            var sb1 = new Storyboard();
            var sb2 = new Storyboard();

            Duration duration1 = new Duration(TimeSpan.FromMilliseconds(_duracionSalto));
            Duration duration2 = new Duration(TimeSpan.FromMilliseconds(_duracionSalto / 2));

            DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
            DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();
            DoubleAnimation myDoubleAnimation4 = new DoubleAnimation();

            myDoubleAnimation1.Duration = duration1;
            myDoubleAnimation2.Duration = duration2;
            myDoubleAnimation4.Duration = duration2;

            sb1.Duration = duration1;
            sb2.Duration = duration2;
            sb1.Children.Add(myDoubleAnimation1);
            sb1.Children.Add(myDoubleAnimation2);
            sb2.Children.Add(myDoubleAnimation4);

            Storyboard.SetTarget(myDoubleAnimation1, this);
            Storyboard.SetTarget(myDoubleAnimation2, this);
            Storyboard.SetTarget(myDoubleAnimation4, this);

            switch (Viajero)
            {
                case Personajes.lobo:
                    Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(LoboX)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(LoboY)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(LoboY)"));

                    myDoubleAnimation1.From = LoboX;
                    myDoubleAnimation1.To = PosicionBalsa;
                    myDoubleAnimation2.From = LoboY;
                    myDoubleAnimation2.To = LoboY + AlturaSalto;
                    myDoubleAnimation4.From = LoboY + AlturaSalto;
                    myDoubleAnimation4.To = LoboY;
                    break;
                case Personajes.cabra:
                    Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(CabraX)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(CabraY)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(CabraY)"));

                    myDoubleAnimation1.From = CabraX;
                    myDoubleAnimation1.To = PosicionBalsa;
                    myDoubleAnimation2.From = CabraY;
                    myDoubleAnimation2.To = CabraY + AlturaSalto;
                    myDoubleAnimation4.From = CabraY + AlturaSalto;
                    myDoubleAnimation4.To = CabraY;
                    break;
                case Personajes.col:
                    Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(ColX)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(ColY)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(ColY)"));

                    myDoubleAnimation1.From = ColX;
                    myDoubleAnimation1.To = PosicionBalsa;
                    myDoubleAnimation2.From = ColY;
                    myDoubleAnimation2.To = ColY + AlturaSalto;
                    myDoubleAnimation4.From = ColY + AlturaSalto;
                    myDoubleAnimation4.To = ColY;
                    break;
                case Personajes.balsa:
                    Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(BalsaX)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(BalsaX)"));
                    Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(BalsaX)"));
                    break;
            }

            sb1.Completed += new EventHandler((object sender, EventArgs e) => sb2.Begin());
            sb2.Completed += new EventHandler(Navegar);
            sb1.Begin();
            return true;
        }

        private void Navegar(object sender, EventArgs e)
        {
            var sb = new Storyboard();
            Duration duration1 = new Duration(TimeSpan.FromMilliseconds(_duracionViaje));
            DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
            DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();
            myDoubleAnimation1.Duration = duration1;
            myDoubleAnimation2.Duration = duration1;
            sb.Duration = duration1;
            sb.Children.Add(myDoubleAnimation1);
            sb.Children.Add(myDoubleAnimation2);
            Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(BalsaX)"));
            Storyboard.SetTarget(myDoubleAnimation1, this);
            myDoubleAnimation1.From = BalsaX;
            myDoubleAnimation1.To = PosicionBalsaContraria;

            switch (Viajero)
            {
                case Personajes.lobo:
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(LoboX)"));
                    Storyboard.SetTarget(myDoubleAnimation2, this);
                    myDoubleAnimation2.From = LoboX;
                    myDoubleAnimation2.To = PosicionBalsaContraria;
                    break;
                case Personajes.cabra:
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(CabraX)"));
                    Storyboard.SetTarget(myDoubleAnimation2, this);
                    myDoubleAnimation2.From = CabraX;
                    myDoubleAnimation2.To = PosicionBalsaContraria;
                    break;
                case Personajes.col:
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(ColX)"));
                    Storyboard.SetTarget(myDoubleAnimation2, this);
                    myDoubleAnimation2.From = ColX;
                    myDoubleAnimation2.To = PosicionBalsaContraria;
                    break;
                case Personajes.balsa:
                    Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(BalsaX)"));
                    Storyboard.SetTarget(myDoubleAnimation2, this);
                    myDoubleAnimation2.From = BalsaX;
                    myDoubleAnimation2.To = PosicionBalsaContraria;
                    break;
            }

            sb.Completed += new EventHandler(SaltarDeLaBalsa);
            sb.Begin();
        }

        void SaltarDeLaBalsa(object sender, EventArgs e)
        {
            CambiarOrilla();

            if (Viajero == Personajes.balsa)
            {
                ViajeTerminado(this, new EventArgs());
            }
            else
            {
                var sb1 = new Storyboard();
                var sb2 = new Storyboard();

                Duration duration1 = new Duration(TimeSpan.FromMilliseconds(_duracionSalto));
                Duration duration2 = new Duration(TimeSpan.FromMilliseconds(_duracionSalto / 2));

                DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
                DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();
                DoubleAnimation myDoubleAnimation4 = new DoubleAnimation();

                myDoubleAnimation1.Duration = duration1;
                myDoubleAnimation2.Duration = duration2;
                myDoubleAnimation4.Duration = duration2;

                sb1.Duration = duration1;
                sb2.Duration = duration2;
                sb1.Children.Add(myDoubleAnimation1);
                sb1.Children.Add(myDoubleAnimation2);
                sb2.Children.Add(myDoubleAnimation4);

                Storyboard.SetTarget(myDoubleAnimation1, this);
                Storyboard.SetTarget(myDoubleAnimation2, this);
                Storyboard.SetTarget(myDoubleAnimation4, this);

                switch (Viajero)
                {
                    case Personajes.lobo:
                        Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(LoboX)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(LoboY)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(LoboY)"));

                        myDoubleAnimation1.From = LoboX;
                        myDoubleAnimation1.To = PosicionOrilla;
                        myDoubleAnimation2.From = LoboY;
                        myDoubleAnimation2.To = LoboY + AlturaSalto;
                        myDoubleAnimation4.From = LoboY + AlturaSalto;
                        myDoubleAnimation4.To = LoboY;
                        break;
                    case Personajes.cabra:
                        Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(CabraX)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(CabraY)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(CabraY)"));

                        myDoubleAnimation1.From = CabraX;
                        myDoubleAnimation1.To = PosicionOrilla;
                        myDoubleAnimation2.From = CabraY;
                        myDoubleAnimation2.To = CabraY + AlturaSalto;
                        myDoubleAnimation4.From = CabraY + AlturaSalto;
                        myDoubleAnimation4.To = CabraY;
                        break;
                    case Personajes.col:
                        Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(ColX)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(ColY)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(ColY)"));

                        myDoubleAnimation1.From = ColX;
                        myDoubleAnimation1.To = PosicionOrilla;
                        myDoubleAnimation2.From = ColY;
                        myDoubleAnimation2.To = ColY + AlturaSalto;
                        myDoubleAnimation4.From = ColY + AlturaSalto;
                        myDoubleAnimation4.To = ColY;
                        break;
                    case Personajes.balsa:
                        Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(BalsaX)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(BalsaX)"));
                        Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(BalsaX)"));
                        break;
                }

                sb1.Completed += new EventHandler((object send, EventArgs ea) => sb2.Begin());
                sb2.Completed += new EventHandler(ViajeTerminado);
                sb1.Begin();
            }

        }

        void ViajeTerminado(object sender, EventArgs e)
        {
            switch (Viajero)
            {
                case Personajes.lobo:
                    if (ContenidoRadioButtons[0])
                    {
                        ContenidoRadioButtons[0] = false;
                        ContenidoRadioButtons[1] = true;
                    }
                    else
                    {
                        ContenidoRadioButtons[1] = false;
                        ContenidoRadioButtons[0] = true;
                    }
                    break;
                case Personajes.cabra:
                    if (ContenidoRadioButtons[2])
                    {
                        ContenidoRadioButtons[2] = false;
                        ContenidoRadioButtons[3] = true;
                    }
                    else
                    {
                        ContenidoRadioButtons[3] = false;
                        ContenidoRadioButtons[2] = true;
                    }
                    break;
                case Personajes.col:
                    if (ContenidoRadioButtons[4])
                    {
                        ContenidoRadioButtons[4] = false;
                        ContenidoRadioButtons[5] = true;
                    }
                    else
                    {
                        ContenidoRadioButtons[5] = false;
                        ContenidoRadioButtons[4] = true;
                    }
                    break;
            }

            NotifyPropertyChanged("ContenidoRadioButtons");

            if (SeguirViajando)
            {
                Solucionar();
            }
        }
    }
}
